/*
	This file is part of Vajolet.

    Vajolet is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Vajolet is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Vajolet.  If not, see <http://www.gnu.org/licenses/>
*/

#ifndef _BOARD
#define _BOARD

#include <conio.h>
#include "vajolet.h"
#include "gameline.h"
#include "bitops.h"
#include "hash.h"
#include "data.h"
#include "transposition.h"


class Board{

public:
	enum bitboardIndex{
		occupiedSquares=0,				//0		00000000
		whiteKing=1,					//1		00000001
		whiteQueens=2,					//2		00000010
		whiteRooks=3,					//3		00000011
		whiteBishops=4,					//4		00000100
		whiteKnights=5,					//5		00000101
		whitePawns=6,					//6		00000110
		whitePieces=7,					//7		00000111
		
        emptyBitmap=8,
		blackKing=9,					//9		00001001
		blackQueens=10,					//10	00001010
		blackRooks=11,					//11	00001011
		blackBishops=12,				//12	00001100
		blackKnights=13,				//13	00001101
		blackPawns=14,					//14	00001110
		blackPieces=15,					//15	00001111

		
		lastBitboard=16,

		empty=occupiedSquares

	};




	void getMirroredBoard(Board & dest,Board &src){
		dest.init();
		for(int i=0;i<64;i++){
			if(src.square[whiteIndex[i]]==empty){
				dest.square[blackIndex[i]]=empty;
			}
			else if(src.square[whiteIndex[i]]>emptyBitmap){
				dest.square[blackIndex[i]]=(bitboardIndex)(src.square[whiteIndex[i]]-emptyBitmap);
			}else{
				dest.square[blackIndex[i]]=(bitboardIndex)(src.square[whiteIndex[i]]+emptyBitmap);
			}
			

		}
		dest.castleRights=0;
		if(src.castleRights&wCastleOO){
			dest.castleRights|=bCastleOO;
		}
		if(src.castleRights&wCastleOOO){
			dest.castleRights|=bCastleOOO;
		}
		if(src.castleRights&bCastleOO){
			dest.castleRights|=wCastleOO;
		}
		if(src.castleRights&bCastleOOO){
			dest.castleRights|=wCastleOOO;
		}

		dest.initFromSquares((eNextMove)(src.nextMove-black),src.fiftyMoveCnt,(eCastle)dest.castleRights,0);

	}

private:
	enum gamePhase{
		opening=0,
		endgame=1
	};

	
	
public:
	int PST[lastBitboard][64]; // game phase, piece, square
	
	unsigned long evalCacheLimit;//1048576;
	unsigned long evalPawnCacheLimit;//131072;
	unsigned long probeCalls;
	unsigned long probeGood;
	struct sEvalCache{
		int sc;
		U64 key;
	} *evalCache;//;[evalCacheLimit];

	struct sEvalCachePawn{
		U64 key;
		int sc;
		bitMap attackBitMap[2];
		bitMap passedPawnBitmap[2];
		bitMap weakPawns;
	}*EvalCachePawn;//[evalPawnCacheLimit];
	

	void setMaterialCacheSize(unsigned long size){
		evalCacheLimit=size;
		if(evalCache){
			free(evalCache);
		}
		evalCache=(sEvalCache*)malloc(sizeof(sEvalCache)*evalCacheLimit);

	};
	void insertMaterialCache(U64 key, int s){
		sEvalCache *item;
		item=&evalCache[key&(evalCacheLimit-1)];
		item->key=key;
		item->sc=s;
	};

	bool probeMaterialCache(U64 key,int * s){
	//	probeCalls++;
		sEvalCache *item;
		item=&evalCache[key&(evalCacheLimit-1)];
		if(item->key==key){
			*s=item->sc;
		//	probeGood++;
			return true;
		}
		return false;
	}
	void setPawnCacheSize(unsigned long size){
		evalPawnCacheLimit=size;
		if(EvalCachePawn){
			free(EvalCachePawn);
		}
		EvalCachePawn=(sEvalCachePawn*)malloc(sizeof(sEvalCachePawn)*evalPawnCacheLimit);

	};
	void insertPawnCache(U64 key, int s, bitMap * pawnAttack,bitMap* passedPawnBitmap,bitMap weakPawn){
		sEvalCachePawn *item;
		item=&EvalCachePawn[key&(evalPawnCacheLimit-1)];
		item->key=key;
		item->sc=s;
		item->attackBitMap[0] =pawnAttack[0];
		item->attackBitMap[1] =pawnAttack[1];
		item->passedPawnBitmap[0] = passedPawnBitmap[0];
		item->passedPawnBitmap[1] = passedPawnBitmap[1];
		item->weakPawns=weakPawn;
	};

	bool probePawnCache(U64 key,int * s,bitMap * pawnAttack,bitMap* passedPawnBitmap, bitMap* weakPawn){
		sEvalCachePawn *item;
		item=&EvalCachePawn[key&(evalPawnCacheLimit-1)];
		if(item->key==key){
			*s=item->sc;
			pawnAttack[0]=item->attackBitMap[0];
			pawnAttack[1]=item->attackBitMap[1];
			passedPawnBitmap[0]=item->passedPawnBitmap[0];
			passedPawnBitmap[1]=item->passedPawnBitmap[1];
			*weakPawn=item->weakPawns;

			//probeGood++;
			return true;
		}
		return false;
	}

	
	Board(): transTT(128){
		EvalCachePawn=NULL;
		evalCache=NULL;
		//MAX_SCORE= 2*(Board::PIECESCORE[Board::whiteQueens]+Board::PIECESCORE[Board::whiteRooks]*2 + Board::PIECESCORE[Board::whiteBishops]*2 + Board::PIECESCORE[Board::whiteKnights]*2);
		//MAX_SCORE -=Board::PIECESCORE[Board::whiteBishops];
		//MAX_SCORE=6000;
	};

	/*enum eKingInCheck{
		dontKnow=255,
		Check=true,
		NotCheck=false,
	}kingInCheck;*/

	enum eNextMove{
		white,
		black=blackKing-whiteKing
	}nextMove;

	int castleRights;
	enum eCastle{
		wCastleOO=1,
		wCastleOOO=2,
		bCastleOO=4,
		bCastleOOO=8,
	};

	int epSquare;
	int fiftyMoveCnt;
	U64 hashKey;
	U64	pawnKey;
	U64 materialKey;

	bitMap boards[lastBitboard]; //bitboards indexed by bitboardIndex enum
	
	bitboardIndex square[64];	// board square rapresentation to speed up
	unsigned char pieceCount[lastBitboard];
	score Material;
	//score totalWhitePawns;
	//score totalBlackPawns;
	score totalWhitePieces; 
	score totalBlackPieces;


	unsigned char viewRotated;
	// keys to calculate hash
	static struct HashKeys KEY;

	GamelineRecord gameLine[MAX_GAME_LENGTH];
	int gameLineIndex;

	static const char* PIECENAMES[lastBitboard];
	static const char* PIECENAMESFEN[lastBitboard];
	static /*const */score PIECESCORE[lastBitboard];
	static const char* PIECECHARS[lastBitboard];

	//transTable pawnTT[2];
	transTable transTT;
	//score MAX_SCORE;


	bitMap pinnedPieces;
	bitMap hiddenCheckers;
	bitMap checkers;

	


	void displayFen(void);
	void display(void);
	void init(void);
	void initFromSquares( eNextMove next, int fiftyM, eCastle castleRight, int epSq);
	void setupFen(const char *fen,const char *fencolor,const char *fencastling,const char *fenenpassant, int fenhalfmoveclock, int fenfullmovenumber);
	//debug function used to test incremental update hash
	U64 calcHash();
	U64 calcMatHash();
	U64 calcPawnsHash();
	int checkBoard();

	void makeNullMove(){
		GamelineRecord & glr=gameLine[gameLineIndex++];
		glr.move.packed=0;
		glr.epSquare=epSquare;
		glr.hashKey=hashKey;
		glr.checkers=checkers;
		glr.extended=false;

		hashKey ^= KEY.ep[epSquare]^KEY.side;
		
		epSquare=0;
		nextMove = (eNextMove)(black-nextMove);

	};
	void unmakeMove();
	void makeMove(Move m);
	void unmakeNullMove(){
		GamelineRecord & glr=gameLine[--gameLineIndex];
		hashKey =glr.hashKey;
		epSquare=glr.epSquare;
		checkers=glr.checkers;
		nextMove = (Board::eNextMove)(Board::black-nextMove);
		
	}
	inline bool isOtherKingAttacked(){
		
		/*bitMap KR =KINGRAYS[firstOne(boards[blackKing-nextMove])];
		bool dotest= (KR & boards[whitePieces+nextMove]) &&((BITSET[gameLine[gameLineIndex-1].move.from]|BITSET[gameLine[gameLineIndex-1].move.to])& KR);
		return dotest && */

		/*if(boards[blackKing-nextMove]){
			return isAttackedSingle(boards[blackKing-nextMove], nextMove);
		}
		return 0;*/
		return checkers&boards[whitePieces+nextMove];
	};

	inline bool isOwnKingAttacked(){
		/*if(kingInCheck==dontKnow){
			if(boards[blackKing-nextMove]){
				kingInCheck=(eKingInCheck)isAttackedSingle(boards[whiteKing+nextMove], black-nextMove);	
			}else{
				kingInCheck=(eKingInCheck)0;
			}
		}
		if((kingInCheck && !(checkers&boards[blackPieces-nextMove])) || (!kingInCheck && (checkers&boards[blackPieces-nextMove]))){

			display();
			displayBitmap(checkers);
			displayBitmap(boards[blackPieces-nextMove]);
			std::cout<<kingInCheck<<std::endl;

		}
		return kingInCheck;*/
		return checkers&boards[blackPieces-nextMove];

 
	};
	
	char isAttacked(bitMap targetBitmap, const unsigned char fromSide, bitMap bitMask=0);
	char isAttackedSingle(bitMap targetBitmap, const unsigned char fromSide);
	void showLine(unsigned int n);
	int evalWpawn(unsigned int sq,bitMap * weakPawns);
	int evalBpawn(unsigned int sq,bitMap * weakPawns);
	template <bool trace=false>
	score eval();
	
	bool checkRepetition(){
		for(int i=gameLineIndex-1;i>=0;i--){// TODO si potrebbe fare i-=4;
			if(hashKey==gameLine[i].hashKey){
				return true;
			}
		}
		return false;
	}

	void initPST();

	bitMap getAttackersAndDefenders(int square) const ;
	bitMap getXraySliderAttackersAndDefenders(int square,bitMap removedPiece) const;

	U64 exlusionKey(){
		return hashKey ^ KEY.exclusion;
	}

	unsigned char getGamePhase(){
		score pieces=totalBlackPieces+totalWhitePieces;

		if(pieces>18000){
			return 0;
		}
		if(pieces<900){
			return 128;
		}
		return  (unsigned char)((18000-pieces)*(128.0/(18000-900)));
	}

	void getPinnedPieces();


};


extern Board board;
#endif
